# @Time     : 2025/4/28 11:36
# @Software : Python 3.10
# @About    :
import warnings
from typing import Union, Optional

import markdown
from bs4 import BeautifulSoup
from bs4.element import Tag, NavigableString

from . import tags


class Parser:
    def __init__(self, text: str, strict: bool = False):
        """
        解析Markdown为文档块

        Args:
            text: **str** MarkDown文档
            strict: **bool** 优化MarkDown文档标题等级到最高
        """
        self._md = text
        self._html = markdown.markdown(text, extensions=['markdown.extensions.tables','fenced_code'])
        self._soup = BeautifulSoup(f"{self._html}", 'lxml')  # 解析网页
        self._title_level_map = {}
        self._strict = strict
        if not self._strict:
            self.optimize_title_level()

    def optimize_title_level(self):
        _level = 1
        _document_info = [(f'h{i}', self._soup.find_all(f'h{i}')) for i in range(1, 7)]
        for i, info in enumerate(_document_info):
            if info[1]:
                self._title_level_map[info[0]] = f'h{i+1}'
                _level += 1


    def iter_paragraphs(self):
        yield from self._parser_to_html()

    @property
    def md(self):
        return self._md

    @property
    def html(self):
        return self._html

    @property
    def soup(self):
        return self._soup

    def _parser_p(self, element: Union[Tag, NavigableString]):
        """
        解析 <p> 标签
        Args:
            element:

        Returns:

        """
        _body = tags.Texts().set_style(element.get('class', [None])[0])
        if hasattr(element, 'children'):
            for child in element.children:
                if child.name == 'strong':
                    _body.append(tags.BoldText(child.text))
                elif child.name == 'em':
                    _body.append(tags.ItalicText(child.text))
                elif child.name == 'table':
                    _body.append(self._parser_table(child))
                elif child.name == 'img':
                    _img = self._parser_img(child)
                    _body.append(_img)
                else:
                    _body.append(tags.Text(child.text))
        else:
            _body.append(tags.Text(element.text))
        return _body

    def _parser_blockquote(self, element: Union[Tag, NavigableString]):
        """
        解析 <blockquote> 标签
        Args:
            quote:

        Returns:

        """
        _quote = tags.IntenseQuote().set_style(element.get('class', [None])[0])
        if hasattr(element, 'children'):
            for child in element.children:
                if child.name == 'strong':
                    _quote.append(tags.BoldText(child.text))
                elif child.name == 'em':
                    _quote.append(tags.ItalicText(child.text))
                else:
                    _quote.append(tags.Text(child.text))
            return _quote
        else:
            _quote.append(tags.Text(element.text.strip()))
            return _quote

    def _parser_ul(self, element: Union[Tag, NavigableString]):
        """
        解析<ul>标签
        Args:
            ul:

        Returns:

        """
        _ul = tags.UnorderedList().set_style(element.get('class', [None])[0])
        for item in element.find_all('li'):
            _ul.append(self._parser_p(item))
        if len(_ul) == 1 and not self._strict:
            return _ul[0]
        return _ul

    def _parser_ol(self, element: Union[Tag, NavigableString]):
        """
        解析<ol>标签
        Args:
            ol:

        Returns:

        """
        _ol = tags.OrderedList().set_style(element.get('class', [None])[0])
        for item in element.find_all('li'):
            _ol.append(self._parser_p(item))
        if len(_ol) == 1 and not self._strict:
            return _ol[0]
        return _ol

    def _parser_img(self, element: Union[Tag, NavigableString]):
        return tags.Image(element.get('src'),alt=element.get('alt'))

    def _parser_table(self,element: Union[Tag, NavigableString]):
        return tags.Table().from_html(element)


    def _parser_to_html(self, root: Optional[Union[Tag, NavigableString]]=None):
        """
        解析HTML为文档块
        Args:
            root:

        Returns:

        """
        if root is None:
            root = self._soup.html
        if root:
            if hasattr(root, 'children'):
                for element in root.children:
                    tag = self._title_level_map.get(element.name, element.name)
                    if tag == 'h1':
                        yield tags.Heading1(element.text.strip()).set_style(element.get('class', [None])[0])
                    elif tag == 'h2':
                        yield tags.Heading2(element.text.strip()).set_style(element.get('class', [None])[0])
                    elif tag == 'h3':
                        yield tags.Heading3(element.text.strip()).set_style(element.get('class', [None])[0])
                    elif tag == 'h4':
                        yield tags.Heading4(element.text.strip()).set_style(element.get('class', [None])[0])
                    elif tag == 'h5':
                        yield tags.Heading5(element.text.strip()).set_style(element.get('class', [None])[0])
                    elif tag == 'h6':
                        yield tags.Heading6(element.text.strip()).set_style(element.get('class', [None])[0])
                    elif tag == 'ul':
                        yield self._parser_ul(element)
                    elif tag == 'ol':
                        yield self._parser_ol(element)
                    elif tag == 'p':
                        yield self._parser_p(element)
                    elif tag == 'blockquote':
                        yield self._parser_blockquote(element)
                    elif tag == 'img':
                        yield self._parser_img(element)
                    elif tag == 'table':
                        yield self._parser_table(element)
                    elif tag == 'code':
                        warnings.warn("<code> tag not supported")
                    else:
                        if hasattr(element, 'children'):
                            yield from self._parser_to_html(element)
                        else:
                            yield tags.Text(element.text)
            else:
                yield tags.Text(self._soup.html)
        else:
            yield tags.Text(self._md)
